const roleDao = require("@/dao/role");
const ResourceDao = require("@/dao/resource");
const z_base = require("@/dao/z_base");
const RDBCModel = require("@/model/RDBC");
const {
  AddRoleForUser,
  UpdateRoleLabels,
  DeletePermissionForRole,
  AddPolicies,
  DeletePermissionsForRole,
  BatchDeletePermissions,
} = require("@/utils/casbin");
const { ErrorCode } = require("@/utils/r/code");
const { cloneDeep } = require("lodash");

const role = {
  // * 查询出角色列表(树形)
  async GetTreeList(query) {
    let treeList = [];
    //角色列表 非树形
    let { list, total } = await roleDao.GetList(query);
    // 构造角色列表(树形)
    for (let role of list) {
      //role为角色信息，获取该角色的资源列表和菜单列表
      //根据[角色id]查询[资源id列表]
      role.resource_ids = await roleDao.GetResourcesByRoldId(role.id);
      //根据[角色id]查询[菜单id列表]
      role.menu_ids = await roleDao.GetMenusByRoleId(role.id);
      treeList.push(role);
    }
    return {
      pageSize: +query.page_size,
      pageNum: +query.page_num,
      total: total,
      pageData: treeList,
    };
  },
  // * 新增/更新 角色, 同时维护 role_menu, role_resource, casbin_rule 中的关联数据
  async SaveOrUpdate(body) {
    //检查角色名是否存在
    const existByName = await z_base.GetOne(RDBCModel.Role, {
      name: body.name,
    });
    if (existByName && existByName.id !== body.id) {
      return ErrorCode.ERROR_ROLE_NAME_EXIST;
    }
    let role = cloneDeep(body);
    //针对角色包含新增和修改两部分
    if (role.id) {
      //更新
      await z_base.Update(RDBCModel.Role, role, { id: role.id });
      //! 关联更新 casbin_rule 表中的 v0 (role_label)
      await UpdateRoleLabels(existByName.label, role.label);
    } else {
      // 新增
      let { id } = await z_base.Create(RDBCModel.Role, role);
      // //添加成功的角色，需要设置id
      role.id = id;
      //默认像casbin中添加anonymous和lagout角色
      await AddRoleForUser(role.label, "anonymous");
      await AddRoleForUser(role.label, "logout");
    }
    // * 处理 ResourceIds 资源列表: 先清空和 role_id 相关的, 再重新根据请求数据添加新的
    // 删除 role_resource 和 casbin_rule 中的旧数据,更新情况下删除旧数据添加新数据
    await z_base.Delete(RDBCModel.RoleResource, { role_id: role.id });
    await DeletePermissionsForRole(role.label);
    // * 往 role_resource 和 casbin_rule 中添加最新数据
    if (role.resource_ids && role.resource_ids.length > 0) {
      // 构造 RoleSource po 对象列表, 并往数据库中插入数据
      let rrList = [];
      for (let rid of role.resource_ids) {
        rrList.push({ role_id: role.id, resource_id: rid });
      }
      //添加role_resources批量添加数据
      await z_base.bulkCreate(RDBCModel.RoleResource, rrList);
      //构造批量添加casbin_rule的rules
      let rules = [];
      let resources = await ResourceDao.GetListByIds(role.resource_ids);
      for (let resource of resources) {
        if (resource.url !== "" && resource.request_method !== "") {
          rules.push([role.label, resource.url, resource.request_method]);
        }
      }
      /* [
            [ 'baoan', '/article/list', 'GET' ],
            [ 'baoan', '/article/:id', 'GET' ]
          ] 
        */
      //批量添加策略
      await AddPolicies(rules);
    }
    // *处理 MenuIds 菜单列表: 先清空和 role_id 相关的, 再重新根据请求数据添加新的
    await z_base.Delete(RDBCModel.RoleMenu, { role_id: role.id });
    if (role.menu_ids && role.menu_ids.length > 0) {
      //往role_menu表中插入数据
      let rmList = [];
      for (let menuId of role.menu_ids) {
        rmList.push({ role_id: role.id, menu_id: menuId });
      }

      await z_base.bulkCreate(RDBCModel.RoleMenu, rmList);
    }
    return ErrorCode.OK;
  },
  // * 根据 [id列表] 删除 role 中数据, 同时删除 role_menu, role_resource, casbin_rule 中的关联数据
  async Delete(ids) {
    let labels = await roleDao.GetLabelsByRoleIds(ids);
    if (labels.length === 0) return ErrorCode.OK;
    //从casbin_rule中删除该角色数据
    await BatchDeletePermissions(labels);
    
    //删除角色相关的数据
    await z_base.Delete(RDBCModel.RoleMenu, { role_id: ids });
    await z_base.Delete(RDBCModel.RoleResource, { role_id: ids });
    await z_base.Delete(RDBCModel.Role, { id: ids });
    return ErrorCode.OK;
  },
};

module.exports = role;
